java 10中的局部类型推断,好像并没什么用!(翻译文章)

最近,甲骨文公司以一种新的策略(每六个月)发布一个新的语言版本。这个策略拟定了只对近期更新的三个版本进行长期的支持和维护的说明,如下:

  • 当前稳定版本是java 8
  • java 9 的支持将持续到2018年三月结束,它已经结束了;
  • java 10 的支持将持续到2018年9月
  • 接下来的稳定版本是java 11, 它在2018年将被发布,并且至少可以支持到2023年。

想了解更多,请查看roadmap

我们所在的公司还没有采用java 9,但是就目前来看,完全跳过这个版本,不使用它,而是等待下一个稳定版本是一个更好的选择。除了java 自己的版本变更外,还有很多其他的核心类库,比如Spring Boot 的版本,所以我们更需要小心谨慎的切换。那么,基于这些不可预期的改变,我决定先看一下java 10中的变化,在即将到来的版本中最主要的功能是本地类型推理.

我们看一下它的语法:

List<User> list = new ArrayList<User>();
// or since Java 7
List<User> list = new ArrayList<>();

基本上,本地类型推断可以这么写:

// left side type is inferred from the right side and will be ArrayList
var userList = new ArrayList();

这也就是说,这样的代码的严格约束没那么严格了,但是你更需要花费更多的精力去关注你变量或者方法的命名了。因为这个”var”不具备自描述性。

本地类型推断这个功能有很多编程语言早就在用了,比如Scala,C# 还有Kotlin. 光从这个方面说,java还是比较落后的,所以它决定也要支持这个。当然,这个过程中有一些比较激烈的讨论。比如:

  • 在Koltin 或Scala中,使用val来表示本地常量,使用var来表示变量
  • 使用const 或者 final表示本地常量,var表示变量
  • 使用final var 表示常量,使用var表示变量
  • 使用let,def或者 :=;
  • 等等。

最后,还是决定保持了最接近的语法形式,让var表示变量,final var表示本地常量,下边是java 10的一个例子:

// example 1 - list is a List<User> type
List<User> list = new ArrayList<>();
// example 2 - userList is an ArrayList<Object> type, so you lose type information
var userList = new ArrayList<>();
// example 3 - userListFixed is an ArrayList<User> type, so you keep type information
var userListFixed = new ArrayList<User>();

在这两个列子中,直接使用左边的类型编译是不能推断出list的泛型的的。所以默认类型就是Object。所以你从List中获取数据iterm时就会困扰你。

其实,本地类型推断这个功能对于数据转化信息是有帮助的。比如,你想要转化一种类型的对象变成另一种类型(可能有不同的属性),在jdk8中可以使用匿名类来做,你只能在一个stream范围域进行操作,举个例子,一些新的属性将通过内部管道流中进行操作,而不能通过外部进行操作的。

List<User> users = Arrays.asList(
               new User("Elisabeth", "Bennett", 20),
               new User("Jane", "Bennett", 22),
               new User("Mary", "Bennett", 18),
               new User("Kitty", "Bennett", 17),
               new User("Lydia", "Bennett", 15)
       );
       users.stream()
               .map(u ->
                       new Object() {
                           String fullName = u.firstName + " " + u.lastName;
                           boolean canDrink = u.age >= 18;
                       })
               .forEach(u -> {
                   if (u.canDrink) {
                       System.out.println("+ " + u.fullName + " is of age and can drink");
                   } else {
                       System.out.println("- " + u.fullName + " is not of age and cannot drink");
                   }
               });
       /*  Output will be
        * + Elisabeth Bennett is of age and can drink
        * + Jane Bennett is of age and can drink
        * + Mary Bennett is of age and can drink
        * - Kitty Bennett is not of age and cannot drink
        * - Lydia Bennett is not of age and cannot drink
        */

在java 10中,你就可以在stream的外部进行操作:

List<User> users = Arrays.asList(
         new User("Elisabeth", "Bennett", 20),
         new User("Jane", "Bennett", 22),
         new User("Mary", "Bennett", 18),
         new User("Kitty", "Bennett", 17),
         new User("Lydia", "Bennett", 15)
 );
 final var objects = users.stream()
         .map(u ->
                 new Object() {
                     String fullName = u.firstName + " " + u.lastName;
                     boolean canDrink = u.age >= 18;
                 })
         .collect(Collectors.toUnmodifiableList());
 // do something with the users...
 System.out.println();
 for (var o : objects) {
     if (o.canDrink) {
         System.out.println("+ " + o.fullName + " is of age and can drink");
     } else {
         System.out.println("- " + o.fullName + " is not of age and cannot drink");
     }
 }

所以,在这个stream关闭的时候,被映射的属性仍然是可以连接的。但是,因为它是本地类型推断,所以你不能在当前方法之外使用它。关键字var不能在方法声明中使用,所以你不能传一个var到一个方法中。var不意味着是模糊的动态类型,它仍然是一个强类型,就是一个语法糖,这些推断类型将在编译的时候进行替换的。

就我个人而言,我觉得由于历史根源的很多阻碍原因,java向前发展很难,因为它必须兼顾向后兼容性的同时,在进行创新。

原文链接

坚持原创技术分享,您的支持将鼓励我继续创作!